﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using InterPC.SPF.SPFMechanisms;

/// 
/// Contains SPF Record class and SPF Term base class
///

namespace InterPC.SPF {

    /// <summary>
    /// SPF record base class
    /// </summary>
    public class SPFRecord {

        private List<SPFTerm> _Terms;
        /// <summary>
        /// List of terms that the record contain
        /// </summary>
        public SPFTerm[] Terms { get { return _Terms.ToArray(); } }

        /// <summary>
        /// Return all mechanism terms
        /// </summary>
        public SPFMechanismTerm[] Mechanisms {
            get {
                return Terms.Where(t => t.Type == SPFTermType.Mechanism).Select(t => (SPFMechanismTerm)t).ToArray();
            }
        }

        /// <summary>
        /// Return all modifier terms
        /// </summary>
        public SPFModifierTerm[] Modifiers {
            get {
                return Terms.Where(t => t.Type == SPFTermType.Modifier).Select(t => (SPFModifierTerm)t).ToArray();
            }
        }

        /// <summary>
        /// SPF version of the record
        /// </summary>
        public string SPFVersion { get; set; }

        /// <summary>
        /// Main constructor (for internal parsing purpose only, public users should always use the static Parse method
        /// </summary>
        internal SPFRecord() { }

        /// <summary>
        /// Parse a SPF definition text to a SPFRecord object
        /// If the given SPF definition is invalid, an exception is thrown
        /// </summary>
        /// <param name="datas">the SPF definition to parse</param>
        /// <returns>A SPFRecord object</returns>
        public static SPFRecord Parse(string datas) {

            Regex VersionReg = new Regex(@"^v=(?<version>[^ ]+)( |$)", RegexOptions.IgnoreCase);
            Match M = VersionReg.Match(datas);
            if (!M.Success)
                throw new FormatException(string.Format("SPF record format is incorrect ({0})", datas));

            SPFRecord R = new SPFRecord() { SPFVersion = M.Groups["version"].Value };
            R._Terms = new List<SPFTerm>();

            if (datas.Length > 3 + R.SPFVersion.Length) { // Add terms if any
                string[] Terms = datas.Substring(3 + R.SPFVersion.Length).Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                foreach (string Term in Terms) {
                    R._Terms.Add(SPFTerm.Parse(Term));
                }
            }

            // Check All is last mechanism
            bool HasAllMechanism = R.Mechanisms.Any(m => m.MechanismType == SPFMechanismType.All);
            if (HasAllMechanism && R.Mechanisms.Last().MechanismType != SPFMechanismType.All)
                throw new FormatException("All mechanism not rightmost");

            return R;
        }

        /// <summary>
        /// Returns a normalized SPF definition from the current object
        /// </summary>
        /// <returns>a string SPF definition corresponding to the object</returns>
        public override string ToString() {

            StringBuilder Sb = new StringBuilder("v=");
            Sb.Append(SPFVersion);

            foreach (SPFTerm Term in Terms) {
                Sb.Append(" ");
                Sb.Append(Term);
            }

            return Sb.ToString();

        }
    }

    /// <summary>
    /// SPF term base class
    /// </summary>
    public abstract class SPFTerm {
        
        private SPFTermType _Type;
        /// <summary>
        /// Return the type of the term (mechanism or modifier)
        /// </summary>
        public SPFTermType Type { get { return _Type; } }

        /// <summary>
        /// Constructor for the base term class 
        /// </summary>
        /// <param name="type">Type of the term</param>
        public SPFTerm(SPFTermType type) { _Type = type; }

        /// <summary>
        /// Parse a term string into a term object
        /// Throw an exception if the format is invalid
        /// </summary>
        /// <param name="datas">term string to parse</param>
        /// <returns>an SPFModifierTerm or a SPFMechanismTerm</returns>
        public static SPFTerm Parse(string datas) {

            try {
                try {
                    return SPFModifierTerm.Parse(datas);
                }
                catch (FormatException) { }

                try {
                    return SPFMechanismTerm.Parse(datas);
                }
                catch (FormatException) { }
            }
            catch (Exception ex) {
                throw new FormatException(string.Format(ex.Message, datas));
            }

            throw new FormatException(string.Format("Term format is incorrect ({0})", datas));
        }

    }


}
